package RKUjava.util;
import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;
import RKUjava.datastructures.RKUVector;

public class RKUStringUtils 
{
  /** Return a String representing the double "number" with "precision" number
      of digits after the decimal separator. The method rounds the value to the 
      given precision.
*/
  public static final String decimalFormatter(double number, int precision)
    {
      String s,s2;
      StringTokenizer stk;
      double power = Math.pow(10,precision);
      number = Math.rint(number*power)/power;
      
      stk = new StringTokenizer(""+number,".");
      s = stk.nextElement()+".";
      if (stk.hasMoreElements())
	s2 = (String)stk.nextElement();
      else
	s2 = "0";
      s = s+s2.substring(0, Math.min(precision,s2.length()));
      return s;
    }

  /** Convert a Date to a string representing the time. */
  public static final String timeFormatter(Date thetime)
    {
      SimpleDateFormat formatter = new SimpleDateFormat ("HH:mm:ss");
      formatter.setTimeZone(TimeZone.getTimeZone("ECT"));

      return formatter.format(thetime);
    }

  /** Convert milliseconds to a string representing the time. */
  public static final String timeFormatter(long thetimeinmillis)
    {
      return timeFormatter(new Date(thetimeinmillis));
    }

  /** Calculate the timedifference and return it as a String.
	@param startmillis The number milliseconds elapsed at the beginning
	of the period.
	@param endmillis The number milliseconds elapsed at the end
	of the period.
	@return A string with the format HH:MM:SS. If the time is more than
	99 hours then there will be the sufficient number of digits in the 
	hour part of the string.
    */
  public static final String timeDifference(long startmillis, long endmillis)
    {
      StringBuffer str = new StringBuffer();
      long hours, minutes, seconds, diff;

      diff = endmillis - startmillis;

      hours = (diff/1000)/3600;
      minutes = ((diff/1000)%3600)/60;
      seconds = ((diff/1000)%3600)%60;
		
      if (hours<10)
	str.append("0");
      str.append(hours);
      str.append(":");

      if (minutes<10)
	str.append("0");
      str.append(minutes);
      str.append(":");
		
      if (seconds<10)
	str.append("0");
      str.append(seconds);

      return str.toString();
    }

  /** Convert an int array to a string by calling toString on each entry. */
  public static final String arrayToString(int[] thearray)
    {
      StringBuffer res = new StringBuffer();

      res.append("(");

      for (int i=0;i<thearray.length-1;i++)
	{
	  res.append(thearray[i]);
	  res.append(", ");
	}

      if (thearray.length>0)
	res.append(thearray[thearray.length-1]);
      res.append(")");
      return res.toString();
    }

  /** Convert a double array to a string by calling toString on each entry. */
  public static final String arrayToString(double[] thearray)
    {
      StringBuffer res = new StringBuffer();
      res.append("(");
      for (int i=0;i<thearray.length-1;i++)
	{
	  res.append(thearray[i]);
	  res.append(", ");
	}
      if (thearray.length>0)
	res.append(thearray[thearray.length-1]);
      res.append(")");
      return res.toString();
    }

  /** Convert a double array to a string by calling toString on each entry. 
      Format each entry by calling decimalFormatter on each entry.
  */

  public static final String arrayToString(double[] thearray, int precision)
    {
      StringBuffer res = new StringBuffer();
      res.append("(");
      for (int i=0;i<thearray.length-1;i++)
	{
	  res.append(decimalFormatter(thearray[i], precision));
	  res.append(", ");
	}
      if (thearray.length>0)
	res.append(decimalFormatter(thearray[thearray.length-1], precision));
      res.append(")");
      return res.toString();
    }

  /** Convert an Object array to a string by calling toString on each entry. */
  public static final String arrayToString(Object[] thearray)
    {
      StringBuffer res = new StringBuffer();
      res.append("(");
      for (int i=0;i<thearray.length-1;i++)
	{
	  if (thearray[i]==null)
	    res.append("null");
	  else
	    res.append(thearray[i].toString());
	  res.append(", ");
	}
      if (thearray.length>0) {
	if (thearray[thearray.length-1]==null)
	  res.append("null");
	else
	  res.append(thearray[thearray.length-1].toString());
      }
      res.append(")");
      return res.toString();
    }






  /** Convert an int matrix (2D array) to a string by calling toString on each entry. */
  public static final String matrixToString(int[][] thematrix)
    {
      StringBuffer res = new StringBuffer();

      res.append("(");

      for (int i=0;i<thematrix.length-1;i++)
	{
	  res.append(arrayToString(thematrix[i]));
	  res.append(", ");
	}

      if (thematrix.length>0)
	res.append(arrayToString(thematrix[thematrix.length-1]));
      res.append(")");
      return res.toString();
    }

  /** Convert a double matrix (2D array) to a string by calling toString on each entry. */
  public static final String matrixToString(double[][] thematrix)
    {
      StringBuffer res = new StringBuffer();

      res.append("(");

      for (int i=0;i<thematrix.length-1;i++)
	{
	  res.append(arrayToString(thematrix[i]));
	  res.append(", ");
	}

      if (thematrix.length>0)
	res.append(arrayToString(thematrix[thematrix.length-1]));
      res.append(")");
      return res.toString();
    }

  /** Convert a double matrix (2D array) to a string by calling toString on each entry. 
      Format each entry by calling decimalFormatter on each entry.
  */

  public static final String matrixToString(double[][] thematrix, int precision)
    {
      StringBuffer res = new StringBuffer();

      res.append("(");

      for (int i=0;i<thematrix.length-1;i++)
	{
	  res.append(arrayToString(thematrix[i], precision));
	  res.append(", ");
	}

      if (thematrix.length>0)
	res.append(arrayToString(thematrix[thematrix.length-1]));
      res.append(")");
      return res.toString();
    }

  /** Convert an Object matrix (2D array) to a string by calling toString on each entry. */
  public static final String matrixToString(Object[][] thematrix)
    {
      StringBuffer res = new StringBuffer();

      res.append("(");

      for (int i=0;i<thematrix.length-1;i++)
	{
	  res.append(arrayToString(thematrix[i]));
	  res.append(", ");
	}

      if (thematrix.length>0)
	res.append(arrayToString(thematrix[thematrix.length-1]));
      res.append(")");
      return res.toString();
    }









  /** Prepend zeros to a number and return the string representing it.
	@param number The number to prepend to.
	@param maxnumber The maximal value the <tt>number</tt> can be. This 
	determines how many digits the number has as max.
     */
  public static final String prependZeros(int number, int maxnumber)
    {
      StringBuffer res = new StringBuffer();
      int maxsize, numsize, i;

      maxsize = (""+maxnumber).length();
      numsize = (""+number).length();	
      for (i=0;i<(maxsize-numsize);i++)
	res.append("0");

      res.append(""+number);
      return res.toString();
    }
	

  /** Replace the occurencies of a string with another string.
	@param str The string to replace in.
	@param oldstr The sequence of characters to replace.
	@param newstr The string to insert where <tt>oldstr</tt> is found.
	@return A string where <tt>oldstr</tt> is replaced by <tt>newstr</tt>.
    */
  public static final String replace(String str, String oldstr, String newstr)
    {
      return join(split(str,oldstr),newstr);
    }

  /** Split a string into an array of substrings.
      @param str The string to split.
      @param splitstr The string you want to use as delimiter.
      @return An array of substrings.
  */
  public static final String[] split(String str, String splitstr)
    {
      String[] res;
      RKUVector tmpres = new RKUVector();
      String curstr;
      int curindex = 0, lastindex = 0;
      int indexadder = splitstr.length();

      while(curindex!=-1) {
	curindex = str.indexOf(splitstr, curindex);
	if (curindex==-1) {
	  tmpres.addElement(str.substring(lastindex, str.length()));
	}
	else {
	  tmpres.addElement(str.substring(lastindex, curindex));
	  curindex += indexadder;
	  lastindex = curindex;
	}
      }
      res = new String[tmpres.size()];
      tmpres.copyInto(res);
      return res;
    }

  /** Join a string array to a string. 
      @param strarray The String array to join.
      @param delim A delimiting string to be placed between elements from the 
      array.
      @return The array and the delimiter combined to a string.
  */
  public static final String join(String[] strarray, String delim)
    {
      StringBuffer res = new StringBuffer();

      for (int i=0;i<strarray.length;i++) {
	res.append(strarray[i]);

	if (i<strarray.length-1)
	  res.append(delim);
      }

      return res.toString();
    }

  /** Remove the first and last character of the string. This is useful 
	for stripping quotes at the beginning and end of a string.
	@param str The string to strip.
	@return str without first and last characters.
    */
  public static final String stripQuotes(String str)
    {
      if (str.length()>=2)
	return str.substring(1,str.length()-1);
      else 
	return "";
    };

  /** Convert a string to an array of doubles. It is assumed that the first and
      last characters of str are non-numbers, that is (), {} or []. Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
  */
  public static final double[] stringToDoubleArray(String str)
    {
      return stringToDoubleArray(str, ",");
    }

  /** Convert a string to an array of doubles using a given delimiter 
      string. It is assumed that the first and
      last characters of str are non-numbers, that is (), {} or [].<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
      @param delim The delimiter string.
  */
  public static final double[] stringToDoubleArray(String str, String delim)
    {
      String curvalue;
      StringTokenizer stoken;
      double[] res;
      int i;

      curvalue = str.substring(1,str.length()-1);
      stoken = new StringTokenizer(curvalue, delim);
      res = new double[stoken.countTokens()];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = (new Double((stoken.nextToken()).trim())).doubleValue();
	i++;
      }
      return res;
    }

  /** Convert a string to a 2-dimensional array of doubles. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are non-numbers, that is left parenthesises are
      (, { or [ and right parenthesises are one of ), } or ].  Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
  */
  public static final double[][] stringToDoubleMatrix(String str)
    {
      return stringToDoubleMatrix(str, ",");
    }

  /** Convert a string to a 2-dimensional array of doubles. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are non-numbers, that is left parenthesises are
      (, { or [ and right parenthesises are one of ), } or ].<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
      @param delim The delimiter string. (Only one char!)
  */
  public static final double[][] stringToDoubleMatrix(String str, String delim)
    {
      char[] carr = str.toCharArray();
      int i, level;
      double[][] res;
      char internaldelim = 'Z', givendelim;
      String curvalue;
      StringTokenizer stoken;

      givendelim = delim.charAt(0);

      if (givendelim == internaldelim) 
	internaldelim = 'Y';

      level = 0;

      // Alter some of the delimiters
      for (i=0;i<carr.length;i++) {
	if (carr[i] == '{' || carr[i] == '(' ||carr[i] == '[') 
	  level++;
	else if (carr[i] == ']' || carr[i] == ')' ||carr[i] == '}') 
	  level--;
	else if (level==1 && carr[i] == givendelim) 
	  carr[i] = internaldelim;
      }

      curvalue = new String(carr, 1, carr.length-2);
      stoken = new StringTokenizer(curvalue, ""+internaldelim);
      res = new double[stoken.countTokens()][];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = stringToDoubleArray((stoken.nextToken()).trim(),delim);
	i++;
      }
      return res;

    }

  /** Convert a string to an array of strings. It is assumed that the first and
      last characters of str are parenthesises, that is (), {} or [].  Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed. The substrings may
      not contain the character ','.
      @param str The string to be parsed.
  */
  public static final String[] stringToStringArray(String str)
    {
      return stringToStringArray(str, ",");
    }

  /** Convert a string to an array of strings using a given delimiter 
      string. It is assumed that the first and
      last characters of str are parenthesises, that is (), {} or [].  Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed. The substrings 
      may not contain the delimiting string.
      @param str The string to be parsed.
      @param delim The delimiter string
  */
  public static final String[] stringToStringArray(String str, String delim)
    {
      String curvalue;
      StringTokenizer stoken;
      String[] res;
      int i;

      curvalue = str.substring(1,str.length()-1);
      stoken = new StringTokenizer(curvalue, delim);
      res = new String[stoken.countTokens()];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = new String((stoken.nextToken()).trim());
	i++;
      }
      return res;
    }

  /** Convert a string to a 2-dimensional array of strings. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are parenthesises, that is () {} or []. Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed. The
      substrings may not contain the character ','.
      @param str The string to be parsed.
  */
  public static final String[][] stringToStringMatrix(String str)
    {
      return stringToStringMatrix(str, ",");
    }

  /** Convert a string to a 2-dimensional array of strings. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are parenthesises, that is (), {} or [].<br>
      <b>NOTE:</b> No validation of the string is performed. The substrings
      may not contain the delimiting character.
      @param str The string to be parsed.
      @param delim The delimiter string. (Only one char!)
  */
  public static final String[][] stringToStringMatrix(String str, String delim)
    {
      char[] carr = str.toCharArray();
      int i, level;
      String[][] res;
      char internaldelim = '\u2700', givendelim;
      String curvalue;
      StringTokenizer stoken;

      givendelim = delim.charAt(0);

      if (givendelim == internaldelim) 
	internaldelim = '\u27f0';

      level = 0;

      // Alter some of the delimiters
      for (i=0;i<carr.length;i++) {
	if (carr[i] == '{' || carr[i] == '(' ||carr[i] == '[') 
	  level++;
	else if (carr[i] == ']' || carr[i] == ')' ||carr[i] == '}') 
	  level--;
	else if (level==1 && carr[i] == givendelim) 
	  carr[i] = internaldelim;
      }

      curvalue = new String(carr, 1, carr.length-2);
      stoken = new StringTokenizer(curvalue, ""+internaldelim);
      res = new String[stoken.countTokens()][];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = stringToStringArray((stoken.nextToken()).trim(),delim);
	i++;
      }
      return res;
    }






  /** Convert a string to a 3-dimensional array of doubles. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are non-numbers, that is left parenthesises are
      (, { or [ and right parenthesises are one of ), } or ].  Delimiter is ",".<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
  */
  public static final double[][][] stringToDouble3DMatrix(String str)
    {
      return stringToDouble3DMatrix(str, ",");
    }

  /** Convert a string to a 3-dimensional array of doubles. 
      It is assumed that the first and last characters of str 
      (and subarrays in str) are non-numbers, that is left parenthesises are
      (, { or [ and right parenthesises are one of ), } or ].<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
      @param delim The delimiter string. (Only one char!)
  */
  public static final double[][][] stringToDouble3DMatrix(String str, String delim)
    {
      char[] carr = str.toCharArray();
      int i, level;
      double[][][] res;
      char internaldelim = '\u2700', givendelim;
      String curvalue;
      StringTokenizer stoken;

      givendelim = delim.charAt(0);

      level = 0;

      // Alter some of the delimiters
      for (i=0;i<carr.length;i++) {
	if (carr[i] == '{' || carr[i] == '(' ||carr[i] == '[') 
	  level++;
	else if (carr[i] == ']' || carr[i] == ')' ||carr[i] == '}') 
	  level--;
	else if (level==1 && carr[i] == givendelim) {
	  carr[i] = internaldelim;
	}
      }

      curvalue = new String(carr, 1, carr.length-2);
      stoken = new StringTokenizer(curvalue, ""+internaldelim);
      res = new double[stoken.countTokens()][][];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = stringToDoubleMatrix((stoken.nextToken()).trim(),delim);
	i++;
      }
      return res;

    }


  /** Convert a string to an array of integers. It is assumed that the
      first and last characters of str are non-numbers, that is (), {}
      or [].  Delimiter is ",".<br> <b>NOTE:</b> No validation of the string is
      performed.
      @param str The string to be parsed.  */
  public static final int[] stringToIntArray(String str)
    {
      return stringToIntArray(str, ",");
    }

  /** Convert a string to an array of integers using a given delimiter 
      string. It is assumed that the first and
      last characters of str are non-numbers, that is (), {} or [].<br>
      <b>NOTE:</b> No validation of the string is performed.
      @param str The string to be parsed.
      @param delim The delimiter string.
  */
  public static final int[] stringToIntArray(String str, String delim)
    {
      String curvalue;
      StringTokenizer stoken;
      int[] res;
      int i;

      curvalue = str.substring(1,str.length()-1);
      stoken = new StringTokenizer(curvalue, delim);
      res = new int[stoken.countTokens()];

      i = 0;
      while(stoken.hasMoreTokens()) {
	res[i] = (new Integer((stoken.nextToken()).trim())).intValue();
	i++;
      }
      return res;
    }

  /** Load a double array from a raw textfile using "\n" as delimiter. 
      This method does not throw exceptions but exits the JVM if errors occur.
  @param filename The file to load.
  @see RKUStringUtils#stringToDoubleArray
  */
  public static final double[] loadDoubleArray(String filename) 
    {
      return loadDoubleArray(filename, "\n");
    }


  /** Load a double array from a raw textfile using <tt>delim</tt> as
  delimiter. This method calls stringToDoubleArray().
  @param filename The file to load.
  @param delim The delimiter to use. 
  @see RKUStringUtils#stringToDoubleArray
  */
  public static final double[] loadDoubleArray(String filename, String delim) 
    {
      StringBuffer data = new StringBuffer();
      FileReader fread;
      BufferedReader bread;
      String curline;
      
      try {
	data.append("{");

	fread = new FileReader(filename);
	bread = new BufferedReader(fread);
	
	if (delim!="\n") {
	  while((curline = bread.readLine())!=null) {
	    data.append(curline);
	  }
	}
	else {
	  while((curline = bread.readLine())!=null) {
	    data.append(curline);
	    data.append("\n");
	  }
	}
	data.append("}");
      }
      catch (Exception e) {
	System.out.println("Exception in loadDoubleArray(\""+filename+"\",\""+delim+"\")");
	System.out.println(e);
	System.exit(0);
      }

      return stringToDoubleArray(data.toString(),delim);
    }



  /** Load a double array from a raw textfile using "\n" as delimiter. 
  @param filename The file to load.
  @see RKUStringUtils#stringToDoubleArray
  */
  public static final double[] loadDoubleArrayEX(String filename) 
  throws FileNotFoundException, IOException
    {
      return loadDoubleArrayEX(filename, "\n");
    }


  /** Load a double array from a raw textfile using <tt>delim</tt> as
  delimiter. This method calls stringToDoubleArray().
  @param filename The file to load.
  @param delim The delimiter to use. 
  @see RKUStringUtils#stringToDoubleArray
  */
  public static final double[] loadDoubleArrayEX(String filename, String delim) 
  throws FileNotFoundException, IOException
    {
      StringBuffer data = new StringBuffer();
      FileReader fread;
      BufferedReader bread;
      String curline;
      
      data.append("{");

      fread = new FileReader(filename);
      bread = new BufferedReader(fread);
	  
      if (delim!="\n") {
	while((curline = bread.readLine())!=null) {
	  data.append(curline);
	}
      }
      else {
	while((curline = bread.readLine())!=null) {
	  data.append(curline);
	  data.append("\n");
	}
      }
      data.append("}");

      return stringToDoubleArray(data.toString(),delim);
    }

}



